#include "config.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define DBG_SUBSYS S_LIBCONTROL

#include "yid.h"
#include "dbg.h"
#include "ramdisk.h"
#include "buffer.h"
#include "sysy_lib.h"

#define RAMDISK_PATH "/dev/shm/lich4/ramdisk"

int ramdisk_open(int *_fd, const chkid_t *chkid)
{
        int ret, fd;
        char path[MAX_PATH_LEN];

        sprintf(path, RAMDISK_PATH"/"CHKID_FORMAT, CHKID_ARG(chkid));

        fd = _open(path, O_RDWR | O_CREAT | O_SYNC, 0644);
        if (fd < 0) {
                ret = errno;
                GOTO(err_ret, ret);
        }

        *_fd = fd;

        return 0;
err_ret:
        return ret;
}

int ramdisk_pread(int fd, buffer_t *buf, size_t size, off_t offset)
{
        int ret;
        char *tmp;

        ret = ymalloc((void **)&tmp, size);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        ret = pread(fd, tmp, size, offset);
        if (ret < 0) {
                ret = errno;
                GOTO(err_free, ret);
        }

        mbuffer_copy(buf, tmp, size);
        yfree((void **)&tmp);

        return 0;
err_free:
        yfree((void **)&tmp);
err_ret:
        return ret;
}

int ramdisk_pwrite(int fd, const chkid_t *chkid, const buffer_t *buf, size_t size, off_t offset)
{
        int ret;

        YASSERT(buf->len == size);
        ret = mbuffer_writefile(buf, fd, offset);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        ret = ramdisk_crc_write(chkid, buf, size, offset);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int ramdisk_close(int fd, const chkid_t *chkid)
{
        char path[MAX_PATH_LEN];

        close(fd);

        sprintf(path, RAMDISK_PATH"/"CHKID_FORMAT, CHKID_ARG(chkid));
        unlink(path);

        return 0;
}

int ramdisk_read(const chkid_t *chkid, buffer_t *buf, size_t size, off_t offset)
{
        int ret, fd;

        ret = ramdisk_open(&fd, chkid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = ramdisk_pread(fd, buf, size, offset);
        if (unlikely(ret))
                GOTO(err_close, ret);

        close(fd);

        return 0;
err_close:
        close(fd);
err_ret:
        return ret;
}

int ramdisk_write(const chkid_t *chkid, const buffer_t *buf, size_t size, off_t offset)
{
        int ret, fd;

        ret = ramdisk_open(&fd, chkid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = ramdisk_pwrite(fd, chkid, buf, size, offset);
        if (unlikely(ret))
                GOTO(err_close, ret);

        close(fd);

        return 0;
err_close:
        close(fd);
err_ret:
        return ret;
}

int ramdisk_crc_write(const chkid_t *chkid, const buffer_t *buf, size_t size, off_t offset)
{
        int count, i;
        uint32_t crc;
        off_t off;
        char mem[LICH_BLOCK_SIZE], info[MAX_INFO_LEN];

        YASSERT(buf->len == size);

        if (chkid->type == __RAW_CHUNK__)
                off = offset + chkid->idx * LICH_CHUNK_SPLIT;
        else
                off = offset;

        if (size % LICH_BLOCK_SIZE == 0) {
                count = size / LICH_BLOCK_SIZE;
                for (i = 0; i < count; i++) {
                        if (i % 8 == 0) {
                                sprintf(info, CHKID_FORMAT" (%ld, %ld) LBA:%016lx ", CHKID_ARG(chkid), size, off, off / LICH_BLOCK_SIZE + i);
                        }

                        mbuffer_read(buf, mem, i * LICH_BLOCK_SIZE, LICH_BLOCK_SIZE);
                        crc = crc32_sum(mem, LICH_BLOCK_SIZE);

                        sprintf(info + strlen(info), " %08x", crc);

                        if ((i + 1) % 8 == 0) {
                                DWARN_RAMDISK("%s\n", info);
                        }
                }

                if (i % 8 != 0) {
                        DWARN_RAMDISK("%s\n", info);
                }

                // record subvol bmap data
                /*
                if (chkid->type == __VOLUME_SUB_CHUNK__) {
                        sprintf(info, CHKID_FORMAT" (%ld, %ld) LBA:%016lx ", CHKID_ARG(chkid), size, off, off / LICH_BLOCK_SIZE);
                        mbuffer_read(buf, mem, 0, LICH_BLOCK_SIZE);
                        _hex_print(info + strlen(info), MAX_INFO_LEN - strlen(info), mem, 128);
                        DWARN_RAMDISK("%s\n", info);
                }
                */
        } else {
                // record subvol item data size == 52
                YASSERT(size < LICH_BLOCK_SIZE);
                sprintf(info, CHKID_FORMAT" (%ld, %ld) LBA:%016lx ", CHKID_ARG(chkid), size, off, off / LICH_BLOCK_SIZE);
                mbuffer_read(buf, mem, 0, size);
                _hex_print(info + strlen(info), MAX_INFO_LEN - strlen(info), mem, size);
                DWARN_RAMDISK("%s\n", info);
        }

        return 0;
}
